#include "dmc.h"

/* if you want get a bug, set 0 to 1 */
#define WRITE_BUG 0
#define READ_BUG 0
#define GET_BUG 0
#define SET_BUG 0
/* case 0 is no bug, 1,2,3 are different bugs */
#define DANGLING_CASE   0

static char *dangling_pointer()
$$  /* if want check dangling pointer, must use $$ instead { */

    #if DANGLING_CASE == 0  /* no error */
    char *s = "no dangling pointer!";   /* s store in data segment */
    $return(s)  /* use $return() to check dangling pointer */
    #elif DANGLING_CASE == 1  /* buf cause dangling pointer */
    char s[32] = {};
    strcpy(s, "hello");
    printf("local str: %s\n", s);
    $return(s)
    #elif DANGLING_CASE == 2  /* variable cause dangling pointer */
    int tmp = 0;
    $return(&tmp);
    #elif DANGLING_CASE == 3  /* variable cause dangling pointer */
    struct person {
        char name[32];
        unsigned int age;
        int sex;
    };
    struct person p = {"jason", 23, 0};
    $return(&p);
    #endif // DANGLING_CASE
}

int main(int argc, char *argv[])
{
    dmc_print("welcome!\n");

    dmc_print("test malloc & free\n");
    $define(char* ,p)   /* define one pointer and set to NULL */
    printf("define pointer p:%p\n", p);
    $malloc(p, 32)      /* malloc for p */
    printf("malloc pointer p:%p\n", p);
    $free(p)            /* free p */
    printf("free pointer p:%p\n", p);

    $malloc2(char *, q, 32) /* define and malloc for q */
    printf("malloc2 pointer q:%p\n", q);
    $malloc(q, 128)         /* remalloc q without free */
    printf("again malloc pointer q:%p\n", q);
    memset(q, 0, 32);
    printf("free pointer q:%p double time start\n", q);
    $free(q)
    printf("free pointer q:%p double time first\n", q);
    $free(q)
    printf("free pointer q:%p double time second\n", q);

    dmc_print("test memory access with normal api\n");
    #define BUFLEN 10
    char buf[BUFLEN] = {0,1,2,3,4,5,6,7,8,9};
    printf("lookup buf:%p\n", buf);
    int i;
    for (i = 0; i < BUFLEN; i++) {
        char t;
        $rd(buf, BUFLEN, i, t)  /* read from buf */
        printf("%d ", t);
    }
    printf("\nlookup buf done\n");

    char data;
    #if WRITE_BUG == 1
    $wr(buf, BUFLEN, 10, 10)
    printf("write 10 to buf[10] done\n");
    #else
    $wr(buf, BUFLEN, 0, 10)
    printf("write 10 to buf[0] done\n");
    #endif

    #if READ_BUG == 1
    $rd(buf, BUFLEN, 20, data)   /* read buf[0] to data */
    printf("read buf[20] to data:%d\n", data);
    #else
    $rd(buf, BUFLEN, 0, data)   /* read buf[0] to data */
    printf("read buf[0] to data:%d\n", data);
    #endif

    $wrp(buf, BUFLEN, &buf[8], &buf[7])
    printf("write buf[8] with ptr to buf[7] done\n");

    $rdp(buf, BUFLEN, &buf[7], data)
    printf("read buf[7] with ptr to data:%d\n", data);

    dmc_print("test memory access with bound api\n");

    printf("lookup buf:%p\n", buf);
    for (i = 0; i < BUFLEN; i++) {
        char t;
        $rd(buf, BUFLEN, i, t)  /* read from buf */
        printf("%d ", t);
    }
    printf("\nlookup buf done\n");

    /* init a bound, need set buf, size, count */
    $bound(buf_bound, buf, sizeof(char), 10)

    char bound_value;
    #if GET_BUG == 1
    $get(buf_bound, 10, bound_value)
    printf("get value:%d from buf[10] \n", bound_value);
    #else
    $get(buf_bound, 0, bound_value)
    printf("get value:%d from buf[0] \n", bound_value);
    #endif
    $get(buf_bound, 8, bound_value)
    printf("get value:%d from buf[8] \n", bound_value);

    $getptr(buf_bound, &buf[1], bound_value)
    printf("get value:%d buf[1] from with ptr \n", bound_value);

    #if SET_BUG == 1
    $set(buf_bound, 20, 10)
    printf("set 10 to buf[20] done\n");
    #else
    $set(buf_bound, 0, 10)
    printf("set 10 to buf[0] done\n");
    #endif
    $set(buf_bound, 8, bound_value)
    printf("set bound_value to buf[8] done\n");

    $setptr(buf_bound, &buf[7], bound_value)
    printf("set bound_value to buf[7] done\n");

    /* define and call dangling_pointer test */
    $define2(char* ,s, dangling_pointer())
    printf("pointer:%p,%s\n",(void *)s, s);

    dmc_print("test over!\n");
    return 0;
}
